﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;
using System.Threading;

namespace Clandestine.Audio
{
    // Buffers are where audio data is stored.
    // In OpenAL, you bind a buffer to a source, and then you can play, pause, and loop sources.
    // There doesn't seem to be any reason you can't have one buffer on multiple sources.
    // When we move on to streaming audio (for music decoding), common practice is to queue multiple short buffers,
    // and when one has been played, dequeue it, fill it full of a new music chunk, and requeue. OpenAL API is conducive to this.
    // We'll probably have 'StreamingBuffer' or something in that case.
    public class Audio
    {
        // Note: Only ONE audio context can be current at any time, throughout the *whole* application.
        // This is quite unlike the per-thread GraphicsContext.
        // As a result, for reasons of simplicity, we'll use a static Mutex internally and not let anyone have access externally.
        // Having public Lock(), Release() methods like we do in Graphics would give quite the wrong impression about how Audio should be used!
        //
        // Simply: In Graphics, we (as in Clandestine.Graphics) do the rendering, and we can only have one gl context on exactly one thread.
        // In Audio, OpenAL has the 'rendering' loop, and we just send off requests for things to happen. Also, context is global.
        // We also have no need particularly for atomic operations when changing sound properties, because ears < eyes.  True story.
        private AudioContext context;
        private static Mutex alMutex = new Mutex();
		
		internal StreamingThread StreamingThread;

		public Audio() : this(new AudioContext(AudioContext.DefaultDevice)) { } // TODO: expose more choices???
		
        internal Audio(AudioContext context)
        {
            this.context = context;
			
			LockALAndMakeActive();
			setListenerValues();
			UnlockAL();
			
			context.CheckErrors();
			
			this.StreamingThread = new StreamingThread(this);
        }
		
		internal void CheckErrors()
		{
			context.CheckErrors();
		}
		
		private void setListenerValues()
		{
			// TODO: expose these.
			AL.Listener(ALListener3f.Position, 0f, 0f, 1f);
			AL.Listener(ALListener3f.Velocity, 0f, 0f, 0f);
			// atX, atY, atZ, upX, upY, upZ
			float[] ori = new float[] { 0f, 0f, -1f, 0f, 1f, 0f };
			AL.Listener(ALListenerfv.Orientation, ref ori);
		}

        internal void LockALAndMakeActive()
        {
            alMutex.WaitOne();
            this.context.MakeCurrent();
        }

        internal void UnlockAL()
        {
            alMutex.ReleaseMutex();
			
			context.CheckErrors();
        }

        // TODO: Dispose() support!
    }
}
